package pishen;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.ListIterator;

import edu.uci.ics.jung.graph.UndirectedGraph;
import edu.uci.ics.jung.graph.UndirectedSparseGraph;

public class Tester {

	public static void main(String[] args) throws IOException {
		//list of the nodes
		ArrayList<Node> nodeList = new ArrayList<Node>();
		ArrayList<Node> targetBase = new ArrayList<Node>();
		UndirectedGraph<Node, Integer> graph = new UndirectedSparseGraph<Node, Integer>();
		int edgeCount = 0;
		//structure for computing the result
		/*
		int[] lifeStat = new int[100];
		int[] remainAgeStat = new int[100];
		int lifeTotal = 0, remainAgeTotal = 0;
		*/
		
		//generate the initial nodes at time=0
		for(int i = 0; i < 3; i++){
			Node node = new Node(); //the life of the node is decided when creating it
			while(node.getRemainLife() == 0){
				node = new Node();
			}	
			nodeList.add(node);
			graph.addVertex(node);
			for(int j = 0; j < 2; j++){
				targetBase.add(node);
			}
		}
		graph.addEdge(edgeCount++, nodeList.get(0), nodeList.get(1));
		graph.addEdge(edgeCount++, nodeList.get(0), nodeList.get(2));
		graph.addEdge(edgeCount++, nodeList.get(1), nodeList.get(2));
		
		BufferedWriter out = new BufferedWriter(new FileWriter("result"));
		
		//run the test
		int time = 0;
		int finalTime = 400;
		while(nodeList.size() < 10000){
			time++;
			System.out.println("time:" + time);
			ListIterator<Node> iter = nodeList.listIterator();
			//remove nodes with remainLife = 0 and subtract other nodes' remainLife by 1
			while(iter.hasNext()){
				Node node = iter.next();
				if(node.getRemainLife() == 0){
					iter.remove();
					while(targetBase.contains(node)){
						targetBase.remove(node);
					}
					graph.removeVertex(node);
					//lifeStat[node.getGivenLife()]++;
				}else{
					node.setRemainLife(node.getRemainLife() - 1);
				}
			}
			
			//add new nodes in this time frame
			
			//the test will end at time = hundredStart+100, hundredStart can be any integer
			//the purpose of hundredStart is to fit property 2
			int numToAdd = 0;
			if(nodeList.size() < 1000){
				numToAdd = (int)(nodeList.size() * 0.5);
			}else{
				numToAdd = (int)(nodeList.size() * 0.015);
			}
			System.out.println("numOfNode:" + nodeList.size());
			for(int i = 0; i < numToAdd; i++){
				//System.out.println("adding node " + i + "/" + numToAdd);
				Node node = new Node();
				int numOfEdgeToAdd = (int)(1);
				nodeList.add(node);
				graph.addVertex(node);
				Node[] nodeBuffer = new Node[numOfEdgeToAdd];
				int[] degreeBuffer = new int[numOfEdgeToAdd];
				//System.out.println("add " + numOfEdgeToAdd + " edges");
				for(int j = 0; j < numOfEdgeToAdd; j++){
					Node target = targetBase.get((int)(targetBase.size() * Math.random()));
					nodeBuffer[j] = target;
					int degreeCount = 0;
					while(targetBase.contains(target)){
						targetBase.remove(target);
						degreeCount++;
					}
					degreeCount++; //for the new added edge
					degreeBuffer[j] = degreeCount;
				}
				for(int j = 0; j < numOfEdgeToAdd; j++){
					for(int k = 0; k < degreeBuffer[j]; k++){
						targetBase.add(nodeBuffer[j]);
					}
					targetBase.add(node);
					graph.addEdge(edgeCount++, node, nodeBuffer[j]);
				}
			}
			
			/*
			if(time < hundredStart){
				for(int i = 0; i < (int)(nodeList.size() * 0.015); i++){
					nodeList.add(new Node());
				}
			}else{
				int year = time - hundredStart;
				double ratioOverAge = 0;
				for(int i = 99 - year; i < 100; i++){
					ratioOverAge += LifeGenerator.getLifeDisTW(i);
				}
				int numOfBirth = (int)(10000 * LifeGenerator.getAgeDisTW(99 - year) / ratioOverAge);
				for(int i = 0; i < numOfBirth; i++){
					nodeList.add(new Node());
				}
			}
			if(time == hundredStart + 99){
				break;
			}
			*/
		}
		
		System.out.println("storing graph");
		ObjectOutputStream objectOut = new ObjectOutputStream(new FileOutputStream("final_graph.ser"));
		objectOut.writeObject(graph);
		objectOut.close();
		System.out.println("finish storing graph");
		
		
		int[] degreeArray = new int[10000];
		for(Node node: nodeList){
			degreeArray[graph.degree(node)]++;
		}
		for(int i = 0; i < degreeArray.length; i++){
			//System.out.println("print " + i);
			out.write("" + degreeArray[i] + "\n");
		}
		out.close();
		
		
		/*
		//life stat, collecting info for property 1
		out.write("life stat:\n");
		for(int i = 0; i < 100; i++){
			out.write(lifeStat[i] + "\n");
		}
		out.newLine();
		
		//remain age stat, collecting info for property 2
		for(Node node: nodeList){
			remainAgeStat[node.getGivenLife() - node.getRemainLife()]++;
		}
		out.write("remain age stat:\n");
		int accum = 0;
		for(int i = 0; i < 100; i++){
			if(i % 5 == 4){
				out.write((remainAgeStat[i] + accum) + "\n");
				accum = 0;
			}else{
				accum += remainAgeStat[i];
			}
		}
		out.close();
		
		//count the number of instance, collecting info for computing KL
		for(int i = 0; i < 100; i++){
			lifeTotal += lifeStat[i];
			remainAgeTotal += remainAgeStat[i];
		}
		
		//KL for life
		double klLife = 0;
		for(int i = 0; i < 100; i++){
			klLife += (lifeStat[i] / (double)lifeTotal) * Math.log((lifeStat[i] / (double)lifeTotal) / LifeGenerator.getLifeDisTW(i));
			klLife += LifeGenerator.getLifeDisTW(i) * Math.log(LifeGenerator.getLifeDisTW(i) / (lifeStat[i] / (double)lifeTotal));
		}
		System.out.println("KL for life:" + klLife);
		
		//LK for remain age
		double klAge = 0;
		for(int i = 0; i < 100; i++){
			if(remainAgeStat[i] != 0){
				klAge += (remainAgeStat[i] / (double)remainAgeTotal) * Math.log((remainAgeStat[i] / (double)remainAgeTotal) / LifeGenerator.getAgeDisTW(i));
				klAge += LifeGenerator.getAgeDisTW(i) * Math.log(LifeGenerator.getAgeDisTW(i) / (remainAgeStat[i] / (double)remainAgeTotal));
			}
		}
		System.out.println("KL for age: " + klAge);
		
		//number of nodes in the end
		System.out.println("num:" + nodeList.size());
		*/
	}

}
